/*						
Some notes of original program:

...
Note that CFB mode is not well tested nor defined by AES, so using the
Twofish MODE_CFB it not recommended.  If you wish to implement a CFB mode,
build it external to the Twofish code, using the Twofish functions only
in ECB mode.

Below is a sample piece of code showing how the code is typically used
to set up a key, encrypt, and decrypt.  Error checking is somewhat limited
in this example.  Pseudorandom bytes are used for all key and text.

*/

/*
Compilar  -->  gcc -g -o tst2 -lz -lssl -D_M_IX86 twofish2.c zlib_use.c myTwofish2.c ../sha2/sha2.c

*/

#include "aes.h"
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include "zlib_use.h"
#include "../sha2/sha2.h"
#include <openssl/rand.h>
#include "myTwofish2.h"


#define MAX_BLK_CNT		4		// max # blocks per call

static int 
saveKey(char *dige, DWORD *iv32, unsigned long fsize, FILE *out)
{
	fwrite(dige,SHA256_DIGEST_SIZE,1,out);
	fwrite(iv32,sizeof(DWORD),BLOCK_SIZE/32,out);
	fwrite(&fsize,sizeof(unsigned long),1,out);

	return 0;
}

static int 
recovKey(char *dige, DWORD *iv32, unsigned long* fsize, FILE *in)
{
	fread(dige,SHA256_DIGEST_SIZE,1,in);
	fread(iv32,sizeof(DWORD),BLOCK_SIZE/32,in);
	fread(fsize,sizeof(unsigned long),1,in);

	return 0;
}



/*
Some notes of original program:

... Unfortunately, the AES API is somewhat clumsy, and it is not entirely
obvious how to use the above functions.   In particular, note that
makeKey() takes an ASCII hex nibble key string (e.g., 32 characters
for a 128-bit key), which is rarely the way that keys are internally
represented.  The reKey() function uses instead the keyInstance.key32
array of key bits and is the preferred method.  In fact, makeKey()
initializes some internal keyInstance state, then parse the ASCII
string into the binary key32, and calls reKey().  To initialize the
keyInstance state, use a 'dummy' call to makeKey(); i.e., set the
keyMaterial parameter to NULL.  Then use reKey() for all key changes.
Similarly, cipherInit takes an IV string in ASCII hex, so a dummy setup
call with a null IV string will skip the ASCII parse.  
*/

static void
set_pw(DWORD *key32, char *pwd)
	{
	int i;

	memset(key32, 0xa5, 16);

	if ( pwd != NULL )
		{
		if ( (i= strlen(pwd)) > 16 )
			i= 16;

		memcpy(key32, pwd, i);
		}
	}




int 
Twofish_enc(int mode, int keySize, char *fname, char *pwd)
{
	keyInstance    ki;					// key information, including tables
	cipherInstance ci;					// keeps mode (ECB, CBC) and IV
	BYTE plainText[MAX_BLK_CNT*(BLOCK_SIZE/8)];		// Plain text. source text
	BYTE cipherText[MAX_BLK_CNT*(BLOCK_SIZE/8)];		// encrypted text.
	FILE *in, *out;						// Descriptores de los archivos.
	char digest[SHA256_DIGEST_SIZE];			// hash de la password
	char buff[128];		/*BYTE*/			// Buffer auxiliar
	unsigned long fsize;					// Tama#o del archivo
	int byteCnt, n_read;

	
	byteCnt= 16;

	sprintf(buff, "%s.gz", fname);
	compress_one_file(fname, buff);				// Comprimir el archivo

	fsize= file_size(buff);					// Tama#o del archivo comprimido

	if ((in= fopen(buff,"rb")) == NULL)
		{
		printf("\nNo puedo abrir el archivo de entrada: %s\n", buff);
		return(-1);
		}

	sprintf(buff,"%s.cry", fname);
	if ((out= fopen(buff,"wb")) == NULL)			// Crear el archivo encriptado
		{
		printf("\nNo puedo abrir el archivo de salida: %s\n", buff);
		return(-1);
		}

	if (makeKey(&ki,DIR_ENCRYPT,keySize,NULL) != TRUE)
		return(-1);					// 'dummy' setup for a 128-bit key

	set_pw(ki.key32, pwd);
	reKey(&ki);						// run the key schedule

	sha256((unsigned char*)pwd, strlen(pwd), (unsigned char*)digest);

	if (cipherInit(&ci,mode,NULL) != TRUE)
		return(-1);					// 'dummy' setup for cipher

	if (mode != MODE_ECB)					// set up random iv (if needed)
		if (RAND_bytes((unsigned char *)ci.iv32, sizeof(ci.iv32)) == 0)
			{
			printf("problemas al generar numero aleatorio\n");
			return(-1);
			}

	saveKey(digest, ci.iv32, fsize, out);

	while ( (n_read=fread(plainText,sizeof(BYTE),byteCnt,in)) > 0)
		{
		if ( n_read < byteCnt )
			memset( plainText + n_read, 0, byteCnt-n_read );		// Padding with "0"
		if (blockEncrypt(&ci, &ki, plainText, byteCnt*8, cipherText) != byteCnt*8)
			return(-1);
		fwrite(cipherText,byteCnt,sizeof(BYTE),out);
		}

	fclose(in);
	fclose(out);

	sprintf(buff, "%s.gz", fname);

	return (remove(buff));
}


int 
Twofish_dec(int mode,int keySize, char *fname, char *pwd)
{
	keyInstance    ki;					// key information, including tables
	cipherInstance ci;					// keeps mode (ECB, CBC) and IV
	BYTE cipherText[MAX_BLK_CNT*(BLOCK_SIZE/8)];		// encrypted text
	BYTE decryptOut[MAX_BLK_CNT*(BLOCK_SIZE/8)];		// text decrypted. recovered.
	int  byteCnt= 16;//, n_read;
	char digest[SHA256_DIGEST_SIZE];			// hash de la password
	char digest_2[SHA256_DIGEST_SIZE];			// hash de la password
	char buff[128];	/*BYTE*/				// Buffer auxiliar
	char buff_2[128];					// Buffer auxiliar
	unsigned long fsize;
	FILE *in, *out;

	if ((in= fopen(fname,"rb")) == NULL)
	{
		printf("\nNo puedo abrir el archivo: %s\n", fname);
		return(-1);
	}

	if (cipherInit(&ci,mode,NULL) != TRUE)
		return(-1);					// 'dummy' setup for cipher
	recovKey(digest, ci.iv32, &fsize, in);

	sha256((unsigned char*)pwd, strlen(pwd), (unsigned char*)digest_2);
	if (memcmp(digest,digest_2, SHA256_DIGEST_SIZE) != 0)
		{
		printf("Password incorrecta ...\n");
		fclose(in);
		return -1;
		}

	if (makeKey(&ki,DIR_DECRYPT,keySize,NULL) != TRUE)
		return(-1);					// 'dummy' setup for a 128-bit key
	set_pw(ki.key32, pwd);
	reKey(&ki);						// run the key schedule
	
	sprintf(buff,"%s.gz", fname);

	if ((out= fopen(buff,"wb")) == NULL)
	{
		printf("\nNo puedo abrir el archivo: %s\n", buff);
		fclose(in);
		return(-1);
	}

	while (fread(cipherText,byteCnt, sizeof(BYTE),in) > 0 )
	{
		if (blockDecrypt(&ci, &ki, cipherText, byteCnt*8, decryptOut) != byteCnt*8)
			return(-1);				

		if (fsize > byteCnt)
			fsize -= byteCnt;
		else
			byteCnt = fsize;

		fwrite(decryptOut,byteCnt, sizeof(BYTE),out);
	}

	fclose(in);
	fclose(out);

	strcpy(buff_2,fname);
	strcpy(buff_2+(strlen(buff_2)-4),".new");

	decompress_one_file(buff, buff_2);
	
	return (remove(buff));					// without error
}

int
privateK_enc(char * pwd)
{
	if( Twofish_enc(MODE_CBC,128, "private.key", pwd)==-1)
	{
		fprintf(stderr, "Cant find [private.key] file.\n");
		return -1;
	}
	
	return !rename("private.key.cry","private.key");/*restore filename*/
	
}

RSA*
privateK_dec(char * pwd)
{
	FILE* fp;
	RSA* rsa = RSA_new();		
	
	if( Twofish_dec(MODE_CBC,128, "private.key", pwd)==-1)
	{
		fprintf(stderr, "Cant find [private.key] file.\n");
		return NULL;
	}

	if((fp=fopen("private.new", "rb"))==NULL)
	{
		fprintf(stderr,"Check you have your private key. See the README file\n");
		return NULL;
	}
	
	if(!PEM_read_RSAPrivateKey(fp, &rsa, 0, NULL))
	{
		fprintf(stderr,"Problem in opening private key");
		return NULL;
	}
	fclose(fp);
	
	remove("private.new");

	return rsa;
}


/*
int main(int num, char *param[])
{
	int keySize= 128;
	char buff[128]; 	//BYTE		// Buffer auxiliar

	srand((unsigned) time(NULL));					// initialize randomize

	if (num < 2)
	{
		printf("falta parametro en linea de comando\n");
		return 1;
	}

	if (Twofish_enc(MODE_CBC,keySize, param[1], "hola ITBA"))
	{
		printf("CBC Failure at keySize=%d",keySize);
		return 1;
	}
	printf("\n\tEncryption passed..  !!!\n");

	sprintf(buff,"%s.cry", param[1]);
	if (Twofish_dec(MODE_CBC,keySize, buff, "hola ITBA"))
	{
		printf("CBC Failure at keySize=%d",keySize); 
		return 1;
	}
	printf("\tDecryption passed..  !!!\n\n");

	return 0;
}*/

